Skip to content

feat(ui): D.1 primitives complete — DeviceRack, SignalChain, MetricTile et al. - #65

Merged
slittycode merged 1 commit into
mainfrom
claude/ui-consistency-overhaul-sHJ73
May 18, 2026
Merged

feat(ui): D.1 primitives complete — DeviceRack, SignalChain, MetricTile et al.#65
slittycode merged 1 commit into
mainfrom
claude/ui-consistency-overhaul-sHJ73

Conversation

@slittycode

Copy link
Copy Markdown
Owner

Summary

PR 2 of 16 in the UI consistency overhaul / Live 12 restyle stack. Lands the rest of the primitive library on top of the D.0 foundation. No consumer migrations in this PR — every existing component is untouched.

New primitives (apps/ui/src/components/ui/)

Primitive Notes
DeviceRack The Live 12 device chrome — title strip + LED + body + optional signal rail. status × density variants. The centerpiece the rest of the migration will build on.
SignalChain Composes DeviceRack + ChainSeparator into the horizontal stage pipeline. Cables animate when a stage is active. AnalysisStatusPanel.tsx:318-470 becomes this in PR 4.
ChainSeparator Cable + arrow between racked devices. Tones idle/active/success; animated variant. Horizontal + vertical orientations.
MetricTile Live compressor/EQ-style parameter display: label eyebrow + tabular-nums value + optional unit, status LED, accent stripe. Drop-in replacement for AccentMetricCard.
MetricBar / MetricBarRow API-parity lift of the existing helpers from MeasurementPrimitives so the consumer migration is mechanical.
DataTable API-parity lift of StyledDataTable (column types renamed DataTableColumn / DataTableProps).
EmptyState Unifies the ad-hoc "no data" / "feature off" / "failed" banners (UnavailableMeasurementCard, session-musician off-banner, etc.).
TimeReadout tabular-nums elapsed + estimate readout for AnalysisStatusPanel and similar.

Storybook coverage

  • Per-primitive default + variant matrix.
  • DeviceRack stories named 01-04 as the "Live 12 Vocabulary" anchors — EmptyShell, WithParameterGrid, InChain, ActiveStateProgression. These exist so future contributors don't reintroduce drift.
  • SignalChain stories cover every stage-status combo (idle / measuring / pitch-active / interpret-active / all-complete / pitch-failed) plus vertical orientation.

Tooltip migration

apps/ui/src/components/Tooltip.tsx is now a re-export shim for ui/Tooltip.tsx. The old custom + Framer Motion implementation has been removed. No current consumers, so no provider mounting needed yet — that lands when the first consumer (D.4+) imports it.

MeasurementPrimitives.tsx → new-primitive mapping table

For future consumer-migration PRs (D.5/D.6):

Old export New primitive
StatusBadge Pill (tone)
DeltaBadge keep specialized (signed-delta number)
MetricBar MetricBar (drop-in)
MetricBarRow MetricBarRow (drop-in)
AccentMetricCard MetricTile (accent + status props)
TokenBadgeList map() of <Pill>
StyledDataTable DataTable (drop-in, rename column types)
OutlinePillButton Button variant="secondary" or "ghost"
UnavailableMeasurementCard EmptyState
LaneContainer / LaneRow / TimeRuler / StatsBar leave in MeasurementPrimitives — these are timeline-lane primitives, not generic chrome
BADGE_TONE_CLASSES drop (Pill owns tone styling)
ACCENT_CARD_CLASSES drop (MetricTile owns accent stripe styling)

Test plan

  • npm run lint — clean
  • npm run test:unit — 605/605 passing
  • npm run build — clean (90.66 KB CSS, 14.75 KB gzipped)
  • npm run build-storybook — clean
  • npm run test:smoke — not run locally (sandbox network blocks Playwright browser download); existing components untouched so smoke tests should pass in CI
  • npm run storybook — boot locally, walk through the 4 DeviceRack anchor stories + the SignalChain status combos, confirm cables animate when active

Up next (PR 3)

D.2 SamplePlayback — kill the 13 raw zinc-*/amber-*/emerald-* instances; outer <section><DeviceRack name="AUDITION SAMPLES">, generate button → <Button variant="primary" ledIndicator>, manifest meta → <Pill tone="neutral">, sample cards → <Panel variant="surface">. ~80 lines changed.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe


Generated by Claude Code

…le et al.

PR 2 of 16 in the UI consistency overhaul. Lands the rest of the
primitive library on top of the D.0 foundation. No consumer migrations
in this PR.

New primitives (apps/ui/src/components/ui/):
- DeviceRack: the Live 12 device chrome (title strip + LED + body +
  optional signal rail). Status × density variants. The centerpiece
  the rest of the migration will build on.
- SignalChain: composes DeviceRack + ChainSeparator into the
  horizontal stage pipeline (queued → active → success cables
  animate). AnalysisStatusPanel becomes this in PR 4.
- ChainSeparator: cable + arrow between racked devices. Tones
  idle/active/success; animated variant.
- MetricTile: Live compressor/EQ-style parameter display. Label
  eyebrow + tabular-nums value + optional unit, status LED, accent
  stripe. Replaces AccentMetricCard.
- MetricBar / MetricBarRow: API-parity lift of the existing helpers
  from MeasurementPrimitives so the consumer migration is mechanical.
- DataTable: API-parity lift of StyledDataTable.
- EmptyState: unifies the ad-hoc "no data" / "feature off" / "failed"
  banners (UnavailableMeasurementCard, session-musician off-banner,
  etc.).
- TimeReadout: tabular-nums elapsed + estimate readout for
  AnalysisStatusPanel and similar.

Storybook coverage:
- Per-primitive default + variant matrix.
- DeviceRack stories named 01-04 as the "Live 12 Vocabulary" anchors
  (EmptyShell, WithParameterGrid, InChain, ActiveStateProgression) —
  exist so future contributors don't reintroduce drift.
- SignalChain stories cover every stage-status combo plus vertical
  orientation.

Tooltip migration:
- components/Tooltip.tsx becomes a re-export shim for ui/Tooltip.tsx.
  Old custom + Framer Motion implementation removed. No current
  consumers, so no provider mounting needed yet — that lands when
  the first consumer (D.4+) imports it.

Verified: lint clean, 605/605 unit tests passing, build clean
(90.66 KB CSS / 14.75 KB gzipped), build-storybook clean.

MeasurementPrimitives.tsx → new-primitive mapping table (for future
consumer-migration PRs):

  StatusBadge          → Pill (tone)
  DeltaBadge           → keep specialized (signed-delta number)
  MetricBar            → MetricBar (drop-in)
  MetricBarRow         → MetricBarRow (drop-in)
  AccentMetricCard     → MetricTile (accent + status props)
  TokenBadgeList       → map() of Pill
  StyledDataTable      → DataTable (drop-in, rename column types)
  OutlinePillButton    → Button variant="secondary" or "ghost"
  UnavailableMetricCrd → EmptyState
  LaneContainer/Row/   → leave in MeasurementPrimitives — these are
  TimeRuler/StatsBar     timeline-lane primitives, not generic chrome.
  BADGE_TONE_CLASSES   → drop (Pill owns tone styling)
  ACCENT_CARD_CLASSES  → drop (MetricTile owns accent stripe styling)

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

@slittycode slittycode left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

Adds 8 new UI primitives to apps/ui/src/components/ui/ and converts Tooltip.tsx to a Radix-backed re-export shim. Pure UI layer — no Phase 1/2/3 code, no backend files, no measurement schema touched. Chromatic passes; Frontend/Backend CI still in-progress at review time but neither is reachable from these changes. PR description reports 605/605 unit tests passing, consistent with no service-layer changes.

Findings

Worth considering

  • DeviceRack.tsx signal rail — double ml-auto (signal rail block, ~L97–L107): when signalIn, railContent, and signalOut are all set simultaneously, both railContent and signalOut carry ml-auto. In a flex row, only the first ml-auto element claims available space; signalOut ends up adjacent to railContent rather than at the far right. None of the stories exercise all three slots together, so it hasn't been caught. Low priority until a consumer needs it, but worth fixing before D.4 when AnalysisStatusPanel migrates to this.

  • Tooltip TooltipProvider latent footgun (Tooltip.tsx re-export shim): the shim correctly documents the Provider requirement in its comment, but there's no runtime guard — a future consumer importing <Tooltip> without <TooltipProvider> will silently render nothing. Zero current consumers (verified), so not a blocker now. Worth a console.warn check in development when the first consumer lands in D.4+.

  • Helper functions without Vitest coverage (MetricBar.tsx:resolvePercent, TimeReadout.tsx:formatMs/formatRange, SignalChain.tsx:cableToneForTransition): these carry real edge-case logic (null/NaN guards, max === min division-by-zero, state table). Project norm for ui/ components is Storybook rather than Vitest unit tests, and all helpers are unexported, so this is consistent with the existing primitives. Flagging only because resolvePercent's max === min guard is the kind of thing that quietly regresses.

Test results

605/605 unit tests passing (per PR description); Chromatic ✅; Frontend + Backend CI in-progress at review time.

Phase boundary check

Clean. No files outside apps/ui/src/components/ were modified. No Phase 1 output schema touched. No Phase 2/3 code changed.


Generated by Claude Code

@slittycode
slittycode merged commit 918611a into main May 18, 2026
3 checks passed
@slittycode
slittycode deleted the claude/ui-consistency-overhaul-sHJ73 branch May 18, 2026 22:34
slittycode pushed a commit that referenced this pull request May 18, 2026
PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills
every raw zinc-* / amber-* / emerald-* / rounded-lg instance in
SamplePlayback.tsx (was 13 occurrences, now 0).

Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main
once PR #65 merges.

Migration:
- Outer <section> → <DeviceRack name="AUDITION SAMPLES"
  subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack
  status reflects panel state — error tone on failure, active tone
  while generating, success tone once a manifest is rendered.
- Original <h3> id-based labelling replaced by aria-label on the
  DeviceRack; the title strip carries the visible name.
- Regenerate <button> → <Button variant="link" size="sm"> in the
  title-strip action slot.
- Generate <button> → <Button variant="primary" size="md"
  ledIndicator> in the body.
- ManifestMeta pill spans → <Pill tone="neutral" leadingDot>.
- SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group"
  title={CATEGORY_LABELS[category]}>.
- SampleCard <li> rounded-md border → <Panel variant="surface"
  padding="md"> (wrapped in a <li> for list semantics).
- confidenceClassFor() className helper → confidenceTone() returning
  a Pill tone (warning for LOW / low-confidence, neutral for MED,
  success for HIGH).
- Code spans for cited Phase 1 field paths now use design-token
  colors (bg-bg-app, border-border, text-text-primary) instead of
  raw zinc-300/zinc-800.
- MIDI download link uses text-accent + underline instead of zinc.

No behavior changes — same callbacks, same conditional rendering, same
content. Chain-of-custody fields (sample.cites.rationale,
sample.cites.phase1Fields) are rendered identically; PURPOSE.md
invariant preserved.

Verified: lint clean, 605/605 unit tests passing, build clean.
Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB
(larger import surface from the ui/ barrel; tree-shaking will tighten
this as more components migrate and the import is shared).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode pushed a commit that referenced this pull request May 18, 2026
PR #65 review flagged that when signalIn, railContent, and signalOut
are all set together, both railContent and signalOut carried ml-auto.
In a flex row only the first ml-auto sibling claims the available
space, so signalOut collapsed against railContent instead of
anchoring to the far right.

Restructure the rail as three fixed slots: a left container for
signalIn, a flex-1 center for railContent, and a right-justified
container for signalOut. Each slot always renders; empty when its
content is absent. Layout is now consistent across all combinations
of the three slot props.

PR #66 doesn't exercise the all-three-slots case (SamplePlayback only
sets status, not the rail), but D.4 (AnalysisStatusPanel migration)
will, so worth fixing now while the primitive has no production
consumers.

Verified: lint clean, 605/605 unit tests passing.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode added a commit that referenced this pull request May 18, 2026
* feat(ui): D.2 migrate SamplePlayback to ui/ primitives

PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills
every raw zinc-* / amber-* / emerald-* / rounded-lg instance in
SamplePlayback.tsx (was 13 occurrences, now 0).

Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main
once PR #65 merges.

Migration:
- Outer <section> → <DeviceRack name="AUDITION SAMPLES"
  subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack
  status reflects panel state — error tone on failure, active tone
  while generating, success tone once a manifest is rendered.
- Original <h3> id-based labelling replaced by aria-label on the
  DeviceRack; the title strip carries the visible name.
- Regenerate <button> → <Button variant="link" size="sm"> in the
  title-strip action slot.
- Generate <button> → <Button variant="primary" size="md"
  ledIndicator> in the body.
- ManifestMeta pill spans → <Pill tone="neutral" leadingDot>.
- SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group"
  title={CATEGORY_LABELS[category]}>.
- SampleCard <li> rounded-md border → <Panel variant="surface"
  padding="md"> (wrapped in a <li> for list semantics).
- confidenceClassFor() className helper → confidenceTone() returning
  a Pill tone (warning for LOW / low-confidence, neutral for MED,
  success for HIGH).
- Code spans for cited Phase 1 field paths now use design-token
  colors (bg-bg-app, border-border, text-text-primary) instead of
  raw zinc-300/zinc-800.
- MIDI download link uses text-accent + underline instead of zinc.

No behavior changes — same callbacks, same conditional rendering, same
content. Chain-of-custody fields (sample.cites.rationale,
sample.cites.phase1Fields) are rendered identically; PURPOSE.md
invariant preserved.

Verified: lint clean, 605/605 unit tests passing, build clean.
Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB
(larger import surface from the ui/ barrel; tree-shaking will tighten
this as more components migrate and the import is shared).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): DeviceRack signal rail uses 3 explicit slots

PR #65 review flagged that when signalIn, railContent, and signalOut
are all set together, both railContent and signalOut carried ml-auto.
In a flex row only the first ml-auto sibling claims the available
space, so signalOut collapsed against railContent instead of
anchoring to the far right.

Restructure the rail as three fixed slots: a left container for
signalIn, a flex-1 center for railContent, and a right-justified
container for signalOut. Each slot always renders; empty when its
content is absent. Layout is now consistent across all combinations
of the three slot props.

PR #66 doesn't exercise the all-three-slots case (SamplePlayback only
sets status, not the rail), but D.4 (AnalysisStatusPanel migration)
will, so worth fixing now while the primitive has no production
consumers.

Verified: lint clean, 605/605 unit tests passing.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

---------

Co-authored-by: Claude <noreply@anthropic.com>
slittycode added a commit that referenced this pull request May 18, 2026
…on) (#67)

* feat(ui): D.2 migrate SamplePlayback to ui/ primitives

PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills
every raw zinc-* / amber-* / emerald-* / rounded-lg instance in
SamplePlayback.tsx (was 13 occurrences, now 0).

Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main
once PR #65 merges.

Migration:
- Outer <section> → <DeviceRack name="AUDITION SAMPLES"
  subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack
  status reflects panel state — error tone on failure, active tone
  while generating, success tone once a manifest is rendered.
- Original <h3> id-based labelling replaced by aria-label on the
  DeviceRack; the title strip carries the visible name.
- Regenerate <button> → <Button variant="link" size="sm"> in the
  title-strip action slot.
- Generate <button> → <Button variant="primary" size="md"
  ledIndicator> in the body.
- ManifestMeta pill spans → <Pill tone="neutral" leadingDot>.
- SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group"
  title={CATEGORY_LABELS[category]}>.
- SampleCard <li> rounded-md border → <Panel variant="surface"
  padding="md"> (wrapped in a <li> for list semantics).
- confidenceClassFor() className helper → confidenceTone() returning
  a Pill tone (warning for LOW / low-confidence, neutral for MED,
  success for HIGH).
- Code spans for cited Phase 1 field paths now use design-token
  colors (bg-bg-app, border-border, text-text-primary) instead of
  raw zinc-300/zinc-800.
- MIDI download link uses text-accent + underline instead of zinc.

No behavior changes — same callbacks, same conditional rendering, same
content. Chain-of-custody fields (sample.cites.rationale,
sample.cites.phase1Fields) are rendered identically; PURPOSE.md
invariant preserved.

Verified: lint clean, 605/605 unit tests passing, build clean.
Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB
(larger import surface from the ui/ barrel; tree-shaking will tighten
this as more components migrate and the import is shared).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): DeviceRack signal rail uses 3 explicit slots

PR #65 review flagged that when signalIn, railContent, and signalOut
are all set together, both railContent and signalOut carried ml-auto.
In a flex row only the first ml-auto sibling claims the available
space, so signalOut collapsed against railContent instead of
anchoring to the far right.

Restructure the rail as three fixed slots: a left container for
signalIn, a flex-1 center for railContent, and a right-justified
container for signalOut. Each slot always renders; empty when its
content is absent. Layout is now consistent across all combinations
of the three slot props.

PR #66 doesn't exercise the all-three-slots case (SamplePlayback only
sets status, not the rail), but D.4 (AnalysisStatusPanel migration)
will, so worth fixing now while the primitive has no production
consumers.

Verified: lint clean, 605/605 unit tests passing.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* feat(ui): D.3 AnalysisStatusPanel → SignalChain (marquee transformation)

PR 4 of 16 in the UI consistency overhaul. The visual hook reviewers
will remember: the analysis pipeline literally renders as a horizontal
Live 12 device chain.

Stacked on PR #66 (D.2 SamplePlayback + DeviceRack rail fix). Branch
already includes the rail fix needed for the all-3-slots layout this
PR exercises.

Migration:
- Outer <div> → <DeviceRack name="ANALYSIS RUN"
  subtitle={'· ' + runId.slice(-8)} status={overallStatus}>.
  Status maps progress tone + isActive to idle/active/success/error.
- Stop button → <Button variant="danger" size="sm" leadingIcon=Square>
  in the title-strip action slot.
- The three stage tiles (measurement / pitchNoteTranslation /
  interpretation) → <SignalChain orientation="horizontal" animated>.
  The animated cables convey "data is flowing left to right" between
  active stages — the literal Live 12 device chain metaphor.
- Stage statuses (running/queued/completed/failed/etc.) map onto the
  SignalChain primitive's smaller vocabulary
  (idle/queued/active/success/error) via toSignalStatus().
- Per-stage retry buttons rendered via Button variant="ghost" in the
  stage device's action slot.
- Non-retryable error codes (e.g. GEMINI_NOT_CONFIGURED) surface as a
  truncated mono span in the action slot with the full message in the
  title attribute — Audit N1 sibling preserved.
- Primary readout (data-testid="status-panel-primary-readout") wraps
  in <Panel variant="inset" padding="sm"> instead of the hand-rolled
  rounded-sm border bg-bg-card div. Test ID preserved verbatim.
- Progress bar disappears as a standalone element — its information
  (elapsed time, estimate range, percent) folds into the DeviceRack's
  bottom signal-flow rail via railContent. Animated SignalChain cables
  + LED-pulse on the active stage + percent in the rail collectively
  carry the "still running" signal.

Preserved verbatim:
- AnalysisStatusPanelProps (no API change for App.tsx).
- computeLiveProgress() pure function and its export — the only
  consumer (tests/services/analysisStatusProgress.test.ts) was driven
  exclusively by this export and continues to pass without change.
- STAGE_LABELS, statusLabel, all stage-status mapping logic.
- The Audit N1 retry-button gating (errorMarkedNonRetryable check).
- The Audit Finding #6 primary-readout placement at the top of the
  body.

Bundle: index chunk grew from 271 KB → 325 KB gz (97.5 KB compressed)
because AnalysisStatusPanel now imports more from the ui/ barrel.
AnalysisResults shrank slightly. Will tighten as more files share the
import.

Verified: lint clean, 605/605 unit tests passing (computeLiveProgress
test suite unaffected), build clean.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* test(ui): unit-cover AnalysisStatusPanel helpers + tighten ready mapping

PR #67 review (REQUEST CHANGES) called out that toSignalStatus() and
rackStatusFromProgress() were new logic without tests, and that 'ready'
mapping to 'queued' was a judgment-call regression (the user already
sees a Retry button in the action slot — the tile reading 'queued'
implies the chain will auto-resume when it actually won't).

Changes:
- Export toSignalStatus, statusLabel, and rackStatusFromProgress from
  AnalysisStatusPanel.tsx so the test suite can import them. Add doc
  comments to each explaining the intent + the per-case mapping
  rationale.
- Remap 'ready' from 'queued' to 'idle'. statusLabel('ready') still
  returns 'READY' so the user sees the correct label inside the tile;
  the LED + cable + box-shadow no longer fake imminent execution.
- New test file tests/services/analysisStatusPanelHelpers.test.ts
  with 23 tests covering every AnalysisStageStatus value, the unknown
  fallback for both helpers, the dominance of failed/success tones in
  rackStatusFromProgress, the running+isActive interaction, and the
  ready-vs-queued separation explicitly.

Test count: 605 → 628.

Verified: lint clean, full unit suite 628/628 passing.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

---------

Co-authored-by: Claude <noreply@anthropic.com>
slittycode added a commit that referenced this pull request May 18, 2026
* feat(ui): D.2 migrate SamplePlayback to ui/ primitives

PR 3 of 16 in the UI consistency overhaul. Worst-drift fix: kills
every raw zinc-* / amber-* / emerald-* / rounded-lg instance in
SamplePlayback.tsx (was 13 occurrences, now 0).

Stacked on top of PR #65 (D.1 primitives). Will be rebased onto main
once PR #65 merges.

Migration:
- Outer <section> → <DeviceRack name="AUDITION SAMPLES"
  subtitle="· Phase 3 heuristic" status={rackStatusFor(...)}>. Rack
  status reflects panel state — error tone on failure, active tone
  while generating, success tone once a manifest is rendered.
- Original <h3> id-based labelling replaced by aria-label on the
  DeviceRack; the title strip carries the visible name.
- Regenerate <button> → <Button variant="link" size="sm"> in the
  title-strip action slot.
- Generate <button> → <Button variant="primary" size="md"
  ledIndicator> in the body.
- ManifestMeta pill spans → <Pill tone="neutral" leadingDot>.
- SampleGroup <h4> → <SectionHeader size="sm" eyebrow="Group"
  title={CATEGORY_LABELS[category]}>.
- SampleCard <li> rounded-md border → <Panel variant="surface"
  padding="md"> (wrapped in a <li> for list semantics).
- confidenceClassFor() className helper → confidenceTone() returning
  a Pill tone (warning for LOW / low-confidence, neutral for MED,
  success for HIGH).
- Code spans for cited Phase 1 field paths now use design-token
  colors (bg-bg-app, border-border, text-text-primary) instead of
  raw zinc-300/zinc-800.
- MIDI download link uses text-accent + underline instead of zinc.

No behavior changes — same callbacks, same conditional rendering, same
content. Chain-of-custody fields (sample.cites.rationale,
sample.cites.phase1Fields) are rendered identically; PURPOSE.md
invariant preserved.

Verified: lint clean, 605/605 unit tests passing, build clean.
Bundle: AnalysisResults chunk grew from 256.79 KB to 311.86 KB
(larger import surface from the ui/ barrel; tree-shaking will tighten
this as more components migrate and the import is shared).

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* fix(ui): DeviceRack signal rail uses 3 explicit slots

PR #65 review flagged that when signalIn, railContent, and signalOut
are all set together, both railContent and signalOut carried ml-auto.
In a flex row only the first ml-auto sibling claims the available
space, so signalOut collapsed against railContent instead of
anchoring to the far right.

Restructure the rail as three fixed slots: a left container for
signalIn, a flex-1 center for railContent, and a right-justified
container for signalOut. Each slot always renders; empty when its
content is absent. Layout is now consistent across all combinations
of the three slot props.

PR #66 doesn't exercise the all-three-slots case (SamplePlayback only
sets status, not the rail), but D.4 (AnalysisStatusPanel migration)
will, so worth fixing now while the primitive has no production
consumers.

Verified: lint clean, 605/605 unit tests passing.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* feat(ui): D.3 AnalysisStatusPanel → SignalChain (marquee transformation)

PR 4 of 16 in the UI consistency overhaul. The visual hook reviewers
will remember: the analysis pipeline literally renders as a horizontal
Live 12 device chain.

Stacked on PR #66 (D.2 SamplePlayback + DeviceRack rail fix). Branch
already includes the rail fix needed for the all-3-slots layout this
PR exercises.

Migration:
- Outer <div> → <DeviceRack name="ANALYSIS RUN"
  subtitle={'· ' + runId.slice(-8)} status={overallStatus}>.
  Status maps progress tone + isActive to idle/active/success/error.
- Stop button → <Button variant="danger" size="sm" leadingIcon=Square>
  in the title-strip action slot.
- The three stage tiles (measurement / pitchNoteTranslation /
  interpretation) → <SignalChain orientation="horizontal" animated>.
  The animated cables convey "data is flowing left to right" between
  active stages — the literal Live 12 device chain metaphor.
- Stage statuses (running/queued/completed/failed/etc.) map onto the
  SignalChain primitive's smaller vocabulary
  (idle/queued/active/success/error) via toSignalStatus().
- Per-stage retry buttons rendered via Button variant="ghost" in the
  stage device's action slot.
- Non-retryable error codes (e.g. GEMINI_NOT_CONFIGURED) surface as a
  truncated mono span in the action slot with the full message in the
  title attribute — Audit N1 sibling preserved.
- Primary readout (data-testid="status-panel-primary-readout") wraps
  in <Panel variant="inset" padding="sm"> instead of the hand-rolled
  rounded-sm border bg-bg-card div. Test ID preserved verbatim.
- Progress bar disappears as a standalone element — its information
  (elapsed time, estimate range, percent) folds into the DeviceRack's
  bottom signal-flow rail via railContent. Animated SignalChain cables
  + LED-pulse on the active stage + percent in the rail collectively
  carry the "still running" signal.

Preserved verbatim:
- AnalysisStatusPanelProps (no API change for App.tsx).
- computeLiveProgress() pure function and its export — the only
  consumer (tests/services/analysisStatusProgress.test.ts) was driven
  exclusively by this export and continues to pass without change.
- STAGE_LABELS, statusLabel, all stage-status mapping logic.
- The Audit N1 retry-button gating (errorMarkedNonRetryable check).
- The Audit Finding #6 primary-readout placement at the top of the
  body.

Bundle: index chunk grew from 271 KB → 325 KB gz (97.5 KB compressed)
because AnalysisStatusPanel now imports more from the ui/ barrel.
AnalysisResults shrank slightly. Will tighten as more files share the
import.

Verified: lint clean, 605/605 unit tests passing (computeLiveProgress
test suite unaffected), build clean.

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

* chore(ui): normalize package-lock.json optional dep dev flags

npm install during PR review pass dropped "dev": true from optional
rollup platform-binary entries — cosmetic lockfile normalization, no
functional change.

https://claude.ai/code/session_01MWRh46jPZ7YbFq4SkPFEwU

---------

Co-authored-by: Claude <noreply@anthropic.com>
slittycode pushed a commit that referenced this pull request May 18, 2026
PR 14 of 16 in the UI consistency overhaul. Final cleanup pass.

Removes three CSS utility classes from index.css:
- .ableton-panel
- .ableton-device
- .ableton-header

All three were retired by D.* migrations — every former consumer now
uses the canonical primitives from components/ui/ (Panel, DeviceRack,
SectionHeader). `grep -rn` against apps/ui/src/ confirms zero
remaining usages.

.ableton-shell and .ableton-toolbar STAY because App.tsx:952,956 still
consumes them for the outer app chrome (the load-bearing
section-grid shell). A focused follow-up could migrate those two to
DeviceRack / a future AppShell primitive; out of scope here.

Deferred (preserved as-is):
- components/MeasurementPrimitives.tsx cannot be fully deleted yet —
  5 consumers still depend on timeline/lane primitives (LaneContainer,
  LaneRow, StatsBar, TimeRuler) that the plan explicitly preserved.
  The shipped primitives (StatusBadge, DeltaBadge, MetricBar,
  MetricBarRow, OutlinePillButton, TokenBadgeList, StyledDataTable,
  AccentMetricCard) also still have consumers in MeasurementDashboard
  and AnalysisResults sections that weren't migrated (StructureLanes,
  HarmonyLanes, AnalysisResults remaining sections). Keep the file
  intact.
- components/Tooltip.tsx stays as a re-export shim; the file isn't
  causing any drift and external imports may still target it.

Verified: lint clean, 628/628 unit tests passing, build clean.

D.* migrations summary (all merged or open at time of this PR):
- D.0 Foundation (#64) ✓
- D.1 Primitives complete (#65) ✓
- D.2 SamplePlayback (#66) ✓
- D.3 SignalChain marquee (#67) ✓
- D.4 FileUpload + Input Source (#69) ✓
- D.5a Results header + metric strip (#71) ✓
- D.5b Mix Chain (#73) ✓
- D.5c Patches (#74) ✓
- D.5d Section headers (#75) ✓
- D.6a Dashboard structure (#76) ✓
- D.6b Dashboard sections (#77) ✓
- D.6c Dashboard headers (#78) ✓
- D.7 SessionMusicianPanel + MixDoctorPanel (#79) — open
- D.8 Signal Monitor + SpectrogramViewer drift (#80) — open
- D.9 DenseDawConcept drift (#81) — open
- D.10 Cleanup (this PR) ✓

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode added a commit that referenced this pull request May 19, 2026
…82)

PR 14 of 16 in the UI consistency overhaul. Final cleanup pass.

Removes three CSS utility classes from index.css:
- .ableton-panel
- .ableton-device
- .ableton-header

All three were retired by D.* migrations — every former consumer now
uses the canonical primitives from components/ui/ (Panel, DeviceRack,
SectionHeader). `grep -rn` against apps/ui/src/ confirms zero
remaining usages.

.ableton-shell and .ableton-toolbar STAY because App.tsx:952,956 still
consumes them for the outer app chrome (the load-bearing
section-grid shell). A focused follow-up could migrate those two to
DeviceRack / a future AppShell primitive; out of scope here.

Deferred (preserved as-is):
- components/MeasurementPrimitives.tsx cannot be fully deleted yet —
  5 consumers still depend on timeline/lane primitives (LaneContainer,
  LaneRow, StatsBar, TimeRuler) that the plan explicitly preserved.
  The shipped primitives (StatusBadge, DeltaBadge, MetricBar,
  MetricBarRow, OutlinePillButton, TokenBadgeList, StyledDataTable,
  AccentMetricCard) also still have consumers in MeasurementDashboard
  and AnalysisResults sections that weren't migrated (StructureLanes,
  HarmonyLanes, AnalysisResults remaining sections). Keep the file
  intact.
- components/Tooltip.tsx stays as a re-export shim; the file isn't
  causing any drift and external imports may still target it.

Verified: lint clean, 628/628 unit tests passing, build clean.

D.* migrations summary (all merged or open at time of this PR):
- D.0 Foundation (#64) ✓
- D.1 Primitives complete (#65) ✓
- D.2 SamplePlayback (#66) ✓
- D.3 SignalChain marquee (#67) ✓
- D.4 FileUpload + Input Source (#69) ✓
- D.5a Results header + metric strip (#71) ✓
- D.5b Mix Chain (#73) ✓
- D.5c Patches (#74) ✓
- D.5d Section headers (#75) ✓
- D.6a Dashboard structure (#76) ✓
- D.6b Dashboard sections (#77) ✓
- D.6c Dashboard headers (#78) ✓
- D.7 SessionMusicianPanel + MixDoctorPanel (#79) — open
- D.8 Signal Monitor + SpectrogramViewer drift (#80) — open
- D.9 DenseDawConcept drift (#81) — open
- D.10 Cleanup (this PR) ✓

https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants